UltraMID Programmer's Reference Guide
                              Revision 1.00.03
                              22 December 1993










                             Forte Technologies
                          1555 East Henrietta Rd. 
                           Rochester, N.Y. 14623
                             FAX (716) 292-6353
                             BBS (716) 427-2921


        NOTICE


        The information contained in this manual is believed to be
        correct.  The manual is subject to change without notice and
        does not represent a commitment on the part of FORTE or
        Advanced Gravis. 


        Neither FORTE nor Advanced Gravis make a warranty of any
        kind with regard to this material, including, but not
        limited to, the implied warranties of merchantability and
        fitness for a particular purpose.  Neither FORTE nor
        Advanced Gravis shall be liable for errors contained herein
        or for incidental or consequential damages in connection
        with the furnishing, performance or use of this material. 


        This document contains proprietary information which is
        protected by copyright.  This manual is Copyright (C)
        1992,1993 by FORTE and Advanced Gravis.  All rights are
        reserved.  No part of this document may be reproduced,
        transmitted, transcribed, stored in a retrieval system, or
        translated into any human or computer language, in any form
        or by any means; electronic, mechanical, magnetic, optical,
        chemical, manual or otherwise, without the expressed written



                                Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 2
        Copyright Notice


        permission of FORTE and Advanced Gravis. 


        Any copying, duplication, selling, or otherwise distributing
        the program or support files described in this manual, other
        than for the limited purposes of system backup and loading
        the program into the computer as part of executing the
        program, is a violation of the software license agreement
        and the law.  Willful violation of the copyright law of the
        United States can result in statutory damages of up to
        $50,000 in addition to actual damages, plus criminal
        penalties of imprisonment for up to one year and/or a
        $10,000 fine.  









































                                 Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 3




                          Chapter 1 - Introduction



             UltraMID is a TSR program that has approximately 20
        functions to support general midi music synthesis, smart
        patch loading, and DMA-driven digital audio playback.
        UltraMID is an alternative to the low-level SDK.  It
        provides very high level access to the UltraSound card so
        that a programmer can write software to synthesize General
        Midi music with minimal effort.  In fact, the UltraMID TSR
        is easier to use than other popular midi processors.
        UltraMID also makes it very easy for programmers to produce
        digital audio sound effects with a minimal amount of
        additional code and memory.  This document is a technical
        document with the following information: A brief overview of
        the UltraSound card; An overview of how the digital audio
        section works; An overview of how the midi section works;
        And Complete documentation on each of the functions in
        UltraMID. 
             Also included with this documentation are some
        programming examples.  The following files should be of some
        interest to you:

              * umwav.c: A .WAV file player in C.

              * aumwav.asm: A .WAV file player written in assembly.

              * ummidi.c: a .MID file player written in C.  This
                example consists of 4 .c files.  The file totsr.c
                contains the UltraMID interface.  This example is
                based on the source code used for playmidi.exe which
                comes with the UltraSound card.

              * umuart.asm: A program that receives data from the
                UltraSound UART and plays the midi data.

        ____________________________________________________________















                                 Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 4




                   Chapter 2 - The UltraSound audio card








        The UltraSound audio card
        ************************************************************
        Block Diagram:


        +---------------------------------------------------------------------+
        |  +---------------+                                                  |
        |  |MEMORY   MEMORY|                              Amplified Out (st)  o
        |  |               |    +-------+                 Line Out (stereo)   o
        |  |MEMORY   MEMORY|----| GF1   |                                     /
        |  |               |    | ASIC  |---I/O Ports/DMA                    |
        |  |MEMORY   MEMORY|    +-------+        / \      Midi/Joystick port |
        |  |               |                    /   \                        |
        |  |MEMORY   MEMORY|                   /     \                        \
        |  +---------------+                  /       \   Mic input (stereo)  o
        |    (Memory hold all sounds)        /         \  Line input (stereo) o
        |_________________________          /        ___\                  ___|
                                 |___16bit___________| |__8bit_____________|  |
                                                                              |

             The heart of the UltraSound card is based on an ASIC
        chip called the GF1.  The GF1 has no general purpose
        processing capabilites.  It is "hardwired" pipelined
        processor which performs a very specific task.  Simply put,
        it interpolates sound data, applies attenuation to produce
        pan effects and volume, sums data, and produces two digital
        signals which are passed on to the analog circuitry to
        produce sound.  The low-level SDK provides routines to write
        to all of the registers in the GF1 chip and to control each
        of these operations.  UltraMID requires no knowledge of the
        GF1 internal process or registers, but it is useful to
        understand a little bit about how this works so that you can
        visualize what's happening "underneath the hood."
             The GF1 has a single speed at which it operates
        internally.  Unlike other digital processors which can play
        sound data at varying speeds, the GF1 uses a completely
        different method for frequency control.  If a sample is
        recorded at 44.1Khz, the GF1 plays that sample by sending
        each byte in order to the DACs.  If you want to play back
        the sound at 22Khz, the GF1 will interpolate values between
        the sound data points in memory to achieve the slower rate.



                                 Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 5
        Chapter 2 - The UltraSound audio card


        The interpolated sample values are then multipled by values
        in the pan register to produce to sample values, one for the
        left ear, and one for the right.  The GF1 then applies
        volume attenuation based on the value of the volume
        register.  The two values are finally converted to and
        analog signal. 
             The GF1 can play up to 32 voices by repeating the steps
        listed above.  The GF1 has 32 seperate sets of registers to
        control volume, pan, and position.  UltraMID uses 24 voices.
        Each voice is independant from the other voices.  They can
        point to different memory, play at different rates, volumes,
        pan positions, and data types.  The voices can play data
        forward, backward, loop forward, loop backward, loop
        bidirectionally.  The volume of each voice can be modified
        in a similar fasion.  Given a start volume, end volume, and
        a rate, the GF1 can automatically make the volume louder,
        softer, or loop from soft to loud, loud to soft, or
        bidirectionally.  All of these features are used in the midi
        engine, however, the UltraMID programmer does not have to
        write any additional code to make use of these features.  


































                                Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 6




                Chapter 3 - Voice Allocation using UltraMID








        Voice allocation using UltraMID. 
        ************************************************************

             The UltraSound card can have up to 32 mono sounds
        playing at one time, or 16 stereo, or combinations.
        UltraMID runs the UltraSound card in a mode where 24 of
        those voices are available.  UltraMID will allow you to
        allocate voices for digital audio using a prioritized
        scheme.  A priority of 0 is the highest priority.  If you
        request a voice with a 0 priority, you will get one, and it
        will remain yours until you give it up.  Any other priority
        means that your voice can get stolen from you if another
        request is made at a higher priority. 
             Voices are also aged.  If you allocate 25 voices with a
        priority of 1, then the 25th voice will be obtained by
        stealing the first voice that was allocated.  This is
        because the first voice has been allocated the longest and
        because UltraMID uses 24 voices.  If a voice was allocated
        as part of a group of voices, e.g.  a stereo sample, and the
        voice gets stolen, then the other voice will get stolen
        also. 
             MIDI voices are allocated dynamically and
        automatically.  Each voice is allocated initially with a
        priority of 1.  The voices priority is lowered as it plays
        through its envelope.  So, a midi note that is fading out
        has a better chance of getting stolen then a new note that
        is sustaining.  For example, if notes A and B start at the
        same time, and note A sustains while note B starts to taper
        off, then note B will be stolen before note A. 

        N  |
        O B|   **********----------
        T A|   ********************
        E  +---+---------------------------------------------------------------
             Time -->    ^- start release for note B.

        Voices for digital audio playback are assigned a priority by
        your application.  






                                 Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 7




             Chapter 4 - Digital Audio playback with UltraMID. 








        Digital Audio playback with UltraMID. 
        ************************************************************

             The UltraSound card has its own DRAM seperate from that
        of the PC which it uses to store data for playback.  Each of
        the voices points to memory locations on the UltraSound
        card.  The UltraSound adds up all of the data at these
        memory locations and sends it to the left and right stereo
        outputs for playback.  In order to play an 8K sample, for
        example, you must allocate memory on the card, transfer the
        sound data to this memory, and start the voice.  You must
        also set the initial pan position (balance), volume, sample
        rate, data type (8, 16, signed, unsigned, stereo, mono).  A
        question that might come to your mind is, what if I want to
        play a sample bigger than 1Mb, let's say, 2Mb.  UltraMID was
        designed especially to handle the case where you want to
        play large samples with limited memory.  UltraMID will allow
        you to allocate a small buffer, lets say 8K for example, and
        then split up your 2MB sample into many smaller chunks. 

        	|                           |
        	|-------------|-------------|
        	0K            4K            8K

        Initially, there is no data in the 8K buffer.  You start by
        reading 4K of data from the PC hard disk, and sending it to
        UltraMID. 

        	|**           S             |
        	|^------------|-------------|
        	0K            4K            8K

        UltraMID will start to DMA the data (*) into the UltraSound
        card at the fastest rate supported by the UltraSound which
        can be up to 680Kb/s.  At the same time, the UltraMID starts
        the voice playing (^). 

        A moment later:






                                Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 8
        Chapter 4 - Digital Audio playback with UltraMID. 


        	|*************S             |
        	|--^----------|-------------|
        	0K            4K            8K

        Notice how the DMA transfer has completed much earlier than
        the voice.  A 44.1Khz voice is 15 times slower than the
        optimal DMA transfer rate.  At this point UltraMID will call
        your application through a far address that you provide and
        ask you for MORE_DATA.  If your application has another
        buffer ready, it will return it to UltraMID, and the next
        DMA transfer will occur. 

        	|***************************S
        	|---------^---|-------------|
        	0K            4K            8K

        As time goes by, the voice will finally reach your original
        first 4K mark and then UltraMID will call you back telling
        you that it no longer needs access to that buffer
        (BUFFER_DONE). 

        	|             **************S
        	|-------------^-------------|
        	0K            4K            8K

        Immediately following the BUFFER_DONE callback, you will
        receive a MORE_DATA callback.  If you respond with more
        data, than things will look like this:

        	|*************S*************Loop
        	|-------------|^------------|
        	0K            4K            8K

        In this example your application passed UltraMID 4K buffers,
        however, UltraMID can take any size buffer and it will
        manage the data internally in the 8K buffer in the same
        fashion for you.  If you had 256K of data on the PC that you
        wanted to play, then you would pass the whole thing to
        UltraMID.  When UltraMID got to the last 4K of the data, it
        would make a callback to you asking for MORE_DATA.  You
        could than respond with another buffer of any size. 
             Recommendations:

              * The UltraSound card can only DMA from PC memory into
                32 byte boundaries in the card.  If you present a
                buffer that is not a multiple of 32 bytes, then
                UltraMID will have to do direct I/O to the card
                which takes more CPU time than a typical DMA.  If
                you make sure your buffer sizes are a multiple of 32
                bytes, than the polled I/O to the card will be
                avoided. 



                                 Forte Technologies



        UltraMID Programmer's Reference Guide                 Page 9
        Chapter 4 - Digital Audio playback with UltraMID. 



              * If the sample you want to play is not entirely in PC
                memory, but you are reading it from disk, then there
                are several things to be aware of.  All hard disks
                have a higher transfer rate then the fastest sound
                you will play on your UltraSound.  However, the
                transfer rate is not the only variable to pay
                attention to.  It takes a relatively long time for
                the data on the spinning disk to find its way
                underneath the read/write head of the drive.  It
                takes much longer than that for the disk arm to move
                from track to track.  It is sometimes better to use
                many small buffers than it is to use 2 very large
                buffers.  In the .WAV player example (aumwav.asm or
                umwav.c), the number and size of the buffers can be
                changed, try adjusting the numbers and try playing
                different .WAV files on different drives.  (The
                assembly version must have a buffer size which is a
                power of 2). 

        There is an example program that you should have received
        with this document that will play a .WAV formatted file
        using UltraMID.  If you are interested in the C version,
        look at umwav.c.  If you are interested in the assembly
        version look at aumwav.asm.  Neither example requires a C
        library.  You should be able to compile the examples use the
        Borland C++ 3.10 compiler or the Borland assembler (tasm). 
             The structure shown below is used to start a digital
        audio playback.  It can be found in the files ultramid.h
        (c), ultramid.inc (masm), and ultmidi.inc (tasm ideal). 

        um_sound_struct struc
        	um_sound_data label dword
        	um_sound_data_off dw ?
        	um_sound_data_seg dw ?
        	um_stereo_mem label dword
        	um_stereo_mem_off dw ?
        	um_stereo_mem_seg dw ?
        	um_sound_len      dd ?
        	um_gf1mem	  dd ?
        	um_pan	          db ?
        	um_volume         dw ?
        	um_sample_rate    dw ?
        	label um_voice word
        	um_priority       dw ?
        	um_data_type      db ?
        	um_callback_addr label dword
        	um_callback_addr_off dw ?
        	um_callback_addr_seg dw ?
        um_sound_struct ENDS




                                 Forte Technologies



        UltraMID Programmer's Reference Guide                Page 10
        Chapter 4 - Digital Audio playback with UltraMID. 


        Here is a brief description of each of the fields of this
        structure:

        * um_sound_data label dword
        * um_sound_data_off dw ? 
        * um_sound_data_seg dw ? 
             A far pointer to your sound data to start playback.
             There is no requirements about alignment of this data
             in PC memory.  You can put it wherever you want, and it
             can even cross a 64K segment boundary. 
        * um_stereo_mem label dword
        * um_stereo_mem_off dw ? 
        * um_stereo_mem_seg dw ? 
             The UltraSound card cannot play interleaved stereo data
             without also jumping through hoops and doing magic
             tricks when running with 24 active voices.  UltraMID
             will split up interleaved stereo data into smaller
             buffers before it DMA's it into the UltraSound card.
             This field should be a far pointer to PC memory which
             contains as much memory as 1/4 of the buffer used on
             the UltraSound card.  In the above example, the
             UltraSound buffer was divided in half for mono
             playback.  In a stereo situation, the buffer is divided
             4 ways.  UltraMID will use this memory for DMA
             transfers.  The example used an 8K buffer, so
             um_stereo_mem should point to a 2K PC buffer for that
             example. 

             	|left         |right        |
             	|------|------|------|------|
             	0K     2K     4K     6K     8K

        * um_sound_len dd ? 
             The length of the sample to be played in bytes.  Be
             careful not to split up a sample when dividing your
             sound data into multiple buffers.  For example, one
             16-bit-stereo sample is 4 bytes.  Therefore
             um_sound_len should be a multiple of 4 for that data
             type. 
        * um_gf1mem       dd ? 
             The address of the memory allocated on the UltraSound
             card.  This is the address where data will be
             transferred for playback.  You can allocate memory with
             the function TSR_ALLOCATE_MEMORY, and free it with 
             TSR_FREE_MEMORY.  
        * um_pan          db ? 
             The balance control for the voice.  0 is all the way to
             the left, and 15 is all the way to the right. 
        * um_volume dw ? 
             A value from 0 to 4095.  This is a logarithmic volume
             scale.  To (approximately) convert from a linear scale



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 11
        Chapter 4 - Digital Audio playback with UltraMID. 


             (0 - 65535) to a log scale:

        		     /  256 * log(65536/linear_volume)   \
        log_volume = 4096 - |  -------------------------------   |
        		     \          log(2.0)                 /

        or from a log scale (0 - 4095) to a linear scale (0 - 65536):

        				    65536
        linear_volume = ---------------------------------------------------
        		       (4096-log_volume) * ( ln(2) / 256 )
        		      e

             The file vol.inc has a short table which can be used to
             implement a linear volume scale. 
        * um_sample_rate dw ? 
             The sample rate that the sound was recorded at. 
        * label um_voice word
        * um_priority dw ? 
             On entry to TSR_START_DIGITAL, this contains the
             priority of the voice.  0 is the highest priority.  If
             you use a priority of 0, the voice will remain
             allocated until you have completed playback of your
             sample.  If you use a lower priority, such as 1, then
             the voice may be stolen when another request is made to
             UltraMID for another voice.  Voices are allocated to
             turn on midi notes and when calls to TSR_START_DIGITAL 
             are made.  If the operation is successful, the voice
             number is returned.  Otherwise a -1 is returned.  The
             voice number is used in following calls to change
             volume, pan, or stop the voice. 
        * um_data_type db ? 
             um_data_type is a mask containing the following values:

        UM_8BIT		EQU 1	; 1 use 8 bit data
        			; 0 use 16 bit data
        UM_PRELOAD	EQU 2	; preload data  (not implemented yet)
        UM_INVERT_MSB	EQU 4	; invert most significant bit during dma
        UM_STEREO	EQU 8	; 1 for stereo data


             The GF1 always plays signed data.  If you want the GF1
             to play unsigned data, then set the UM_INVERT_MSB flag.
             This will cause the data to get converted in hardware
             during the DMA transfer. 

             If you are playing 8 bit mono unsigned data, then you
             would pass to UltraMID: (UM_8BIT OR UM_INVERT_MSB).  If
             you are playing 16 bit signed stereo data, then you
             would pass to UltraMID: (UM_STEREO).  These are the
             most common data types. 



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 12
        Chapter 4 - Digital Audio playback with UltraMID. 



        * um_callback_addr label dword
        * um_callback_addr_off dw ? 
        * um_callback_addr_seg dw ? 
             This should be a far pointer to a routine in your code
             to handle callbacks from UltraMID.  UltraMID will call
             your code to tell you when a buffer has completed
             playback, when it needs more data, and when it is done
             playing and has deallocated the voice.  If you fill
             this field with 0:0, then you won't get a callback.
             The voice will remain playing until the sample has
             completed.  If you plan on calling UltraMID with no
             callback address, then you should use a non-zero
             priority so that voice-stealing can take place. 
             An example of how digital audio works is in the file
        umwav.c (c code) and in aumwav.asm (assembly).  This example
        will play a .WAV file.  





































                                 Forte Technologies



        UltraMID Programmer's Reference Guide                Page 13




                  Chapter 5 - MIDI playback with UltraMID. 








        MIDI playback with UltraMID. 
        ************************************************************
             UltraMID will accept a general MIDI data stream and
        synthesize music from that data.  The data is passed to
        UltraMID through two functions.  If you want to pass
        UltraMID a byte at a time, you would call TSR_MIDI_OUT.  If
        you want to send a string of midi data, then you would call 
        TSR_MIDI_OUT_STR.  
             The UltraSound card has no build in patches
        (instruments).  There is no ROM or FM synthesizer.  All
        patches must be loaded before you start your MIDI sequence.
        The patches can be loaded when UltraMID starts with the -c
        parameter, or your application can load patches with little
        effort using UltraMID. 
             UltraMID has two modes of operations.  If UltraMID is
        started with the "-c" parameter, then a subset of the
        general midi instrument set will be downloaded into the
        card.  The programmer will have to do no additional work to
        get sounds into the card.  This is probably the most
        appropriate mode of operation for people who already have
        existing drivers for other cards and can't find a way to
        load instruments without changing the other drivers.  The
        other mode of operation requires that the programmer load
        the correct patches before playback starts.  UltraMID has
        several functions to help load the patches. 
             TSR_LOAD_MIDI_PATCHES takes a far pointer to the
        beginning of a track from a standard midi file and loads all
        of the patches that will be needed for that track. 
             TSR_LOAD_XMIDI_PATCHES takes a far pointer to the
        beginning of the event list in an xmidi file and loads all
        of the patches that will be needed for that track. 
             TSR_LOAD_PATCH will load a patch from a program change.
        If you know you are going to be doing program changes to 0
        (piano 1), then you would call TSR_LOAD_PATCH with 0. 
             TSR_UNLOAD_PATCH will clear a patch from the
        UltraSounds memory. 
             UltraMID also provides access to the UltraSound's UART
        for MIDI input.  Your application can receive all of the
        MIDI data that reaches the UART by calling a function to set
        up a callback routine.  Your callback routine will be called
        from UltraMID whenever there is MIDI data or an error from



                                 Forte Technologies



        UltraMID Programmer's Reference Guide                Page 14
        Chapter 5 - MIDI playback with UltraMID. 


        the UART.  The function to activate the callback is 
        TSR_SET_MIDI_CALLBACK.  TSR_CLEAR_MIDI_CALLBACK will
        disactivate the callback. 
             Take a look at the ummidi.exe example to see how to
        play standard midi files with UltraMID.  Ummidi.c is the
        main program which calls all of the other parts.  midifile.c
        reads the midi file into memory.  Midiout.c interprets the
        midi data and makes calls to totsr.c which contains all of
        the calls to UltraMID.  Totsr.c also handles the system
        timer.  The file umuart.c contains an example program that
        shows the use of the MIDI UART for MIDI input.  











































                                 Forte Technologies



        UltraMID Programmer's Reference Guide                Page 15




            Chapter 6 - List of functions supported by UltraMID








        ************************************************************


        TSR_START_DIGITAL         equ 0
             This function will play continuous digital data of any
             length.  It supports the following data types:

                   * 8 bit signed mono data

                   * 16 bit signed mono data

                   * 8 bit signed interleaved (l/r) stereo data

                   * 16 bit signed interleaved (l/r) stereo data

                   * 8 bit unsigned mono data

                   * 16 bit unsigned mono data

                   * 8 bit unsigned interleaved (l/r) stereo data

                   * 16 bit unsigned interleaved (l/r) stereo data

             This function will start a playing digital data by
             transferring the data described in the um_sound_struct
             into the UltraSound card at the specified address.  The
             callback address (um_callback_addr, in the
             um_sound_struct) should point to a function that you
             supply.  This function will get for the following
             reasons:

                  1) UM_STOP_SOUND: the voice has been stopped or
                     stolen, and must no longer be used by your
                     application.  If the voice was stolen, and you
                     have more data to play, than another voice must
                     be allocated and started. 

                  2) UM_MORE_DATA: UltraMID needs more data so that
                     there is a smooth playback without clicks.
                     This callback does NOT mean that the last
                     buffer is done playing.  It just means UltraMID



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 16
        Chapter 6 - List of functions supported by UltraMID


                     is ready for the next buffer.  When you get
                     this callback, UltraMID will look at the return
                     value to figure out what to do next.  The list
                     of return values is described below. 

                  3) UM_BUFFER_DONE: UltraMID is done with a buffer.
                     This means that a buffer has completed playing,
                     and you can re-use that PC memory. 

             When you receive the UM_MORE_DATA callback, you can
             take one of the following actions:

                   * UM_STOP_SOUND: This will tell UltraMID that you
                     have no more data available and it is OK to
                     deallocate the voice when the data is done
                     playing.  If you have more data that you want
                     to play, but it is not available (it is still
                     on the hard disk), you can use the next option.
                     You may receive more than one UM_MORE_DATA
                     request even after you return UM_STOP_SOUND.
                     UltraMID will stop sending callbacks only after
                     UltraMID calls YOUR application with
                     UM_STOP_SOUND. 

                   * UM_PAUSE: This will tell UltraMID to pause
                     playback.  This return value may be ignored
                     until UltraMID is done playing all of the data
                     it has shipped to the card.  At this point, the
                     UM_MORE_DATA callback will occur again.  If you
                     return UM_PAUSE again, then it will finally
                     pause.  When eventually you do have more data
                     to play, you can call the function 
                     TSR_PLAY_NEXT_BUFFER to restart the voice with
                     new data.  UM_PAUSE should only be used to
                     prevent UltraMID from deallocating the voice.
                     It is different from the TSR_PAUSE_DIGITAL 
                     command, which pauses playback immediately. 

                   * UM_MORE_DATA: This is the value you should
                     return if you actually have more data for
                     UltraMID to start playing. 

             UltraMID passes your callback routine several
             parameters on the stack.  If you are using C, you can
             use the following prototype:

                  int far um_callback(int reason,
                                      int voice,
             			 unsigned char far * far *buf,
             			 unsigned long far *size,
             			 unsigned short far *rate



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 17
        Chapter 6 - List of functions supported by UltraMID



             If you are using assembly, you can access these
             parameters by creating a stack frame: push bp; mov bp,
             sp; In large memory model:


                   * reason:word [bp+6]

                   * voice:word [bp+8]

                   * buf:dword ptr [bp+10]

                   * size:dword ptr [bp+14]

                   * rate:word ptr [bp+18]


             Descriptions of the callback parameters:


                   * reason: One of the reasons listed above. 

                   * voice: The same number as the voice returned
                     from TSR_START_DIGITAL.  

                   * buff: If you are responding to UM_MORE_DATA
                     with UM_MORE_DATA, then this address should be
                     filled in with the address of your next buffer. 

                   * size: If you are responding to UM_MORE_DATA
                     with UM_MORE_DATA, then this address should be
                     filled in with the size of your next buffer. 

                   * rate: If you are responding to UM_MORE_DATA
                     with UM_MORE_DATA, then this address should be
                     filled in with the sample rate of your next
                     buffer.  This option was added because DIGPAK
                     required double buffering with varying rates
                     for playback.  The rate will not change until
                     the new buffer starts playback.  If you need to
                     modify the rate immediately, call TSR_SET_RATE.  


             input:
                  es:di - far pointer to sound structure
             output:
                  AX will have the voice that was allocated or -1 if
                  no voice was available.  The voice field in the
                  structure will also contain the value. 
        TSR_PLAY_NEXT_BUFFER      equ 1
             If the callback routine is called and you pause the



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 18
        Chapter 6 - List of functions supported by UltraMID


             voice, this routine can be called to send UltraMID the
             next buffer and also continue playback. 
             input:
                  es:di - far pointer to sound structure
             output:
                  none
        TSR_SET_PAN       equ 2
             This function will be ignored for stereo data.  For a
             mono piece, it will let you set the position of the
             sound from 0 left to 15 right.  The total power of the
             left and right line level outputs is maintained as you
             pan from one side to the other. 
             input:
                  cx - voice
                  bx - pan
             output:
                  none
        TSR_SET_VOLUME            equ 3
             This function will let you change the output volume for
             a specific voice.  The range of volumes is from 0 to
             4095.  This call actually does a volume ramp from the
             old volume to the new volume.  It will actually take
             about 1ms (maximum) to reach the desired volume.  This
             will prevent any clicks or pops in the audio.  VERY
             TECHNICAL NOTE: If you split up the 12 bit volume
             number, the most significant four bits are the
             exponent, and the least significant 8 bits are the
             mantissa.  EEEEMMMMMMMM.  Ignoring pan, for a
             peak-to-peak 16-bit sine wave, the line level output
             voltage (RMS) is:

                  Lvrms = 1.7 * (256+M) / (512 * (2 ^ (15-E)))

             which gives you a line level output range from 0 to 1.7
             volts.  This means for a volume of 0, you will get
             -91.7dbv, and for a volume of 4095, you will get
             +4.6dbv.  This means the UltraSound has a range of
             96dB!.  96dB is extremly loud and can hurt your ears. 

             More information on volume control: Since the
             UltraSound card is always summing 24 voices, you should
             keep the volumes below the maximum so that clipping
             doesn't occur.  The highest midi volume is 4016.  The
             digital volume can go up to 4095, but it might cause
             noise in the output if there are other sounds playing.
             You will get good results if you limit your audible
             digital output to the range between 2048 and 4016.  If
             it turns out your MIDI music is too loud, the user can
             lower the midi volume by running UltraMID with the -m
             parameter.  For example, UltraMID -m100. 
             Input:



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 19
        Chapter 6 - List of functions supported by UltraMID


                  cx - voice
                  bx - volume
             output:
                  none
        TSR_SET_RATE              equ 4
             not implemented yet
        TSR_PAUSE_DIGITAL         equ 5
             This function will pause a voice immediately.  It can
             be restarted with TSR_RESTART_DIGITAL.  No volume ramp
             occurs with this function.  If you want to pause a
             playing voice and make sure there are no 'clicks' in
             the audio, then set the volume to 0 before pausing.
             When restarting, set the volume back to where it was. 
             input:
                  cx - voice
             output:
                  none
        TSR_RESTART_DIGITAL       equ 6
             This function will restart a voice that was paused with 
             TSR_PAUSE_DIGITAL.  Take a look at TSR_PAUSE_DIGITAL 
             above for more information. 
             input:
                  cx - voice
             output:
                  none
        TSR_STOP_DIGITAL          equ 7
             This function will stop a playing voice.  The callback
             routine will get called immediately to alert your
             application that the voice has been deallocated. 
             input:
                  cx - voice
             output:
                  none
        TSR_GET_DIGITAL_POSITION equ 8
             This function will give you an approximate location of
             the sample data that is being played for a particular
             voice.  The location will be a far pointer to somewhere
             in your PC buffer. 
             input:
                  cx - voice
             output:
                  ax:dx far pointer to your PC buffer.  The pointer
                  may be normalized and not have the same segment
                  that you passed in. 
        TSR_VOICE_STATUS          equ 9
             not implemented yet
        ; MIDI functions
        TSR_LOAD_MIDI_PATCHES     equ 10
             This function will automatically load the patches that
             will be needed to play a midi track from a standard
             midi file. 



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 20
        Chapter 6 - List of functions supported by UltraMID


             input:
                  es:di is far pointer to the midi track just after
                  the Mtrk header and length. 
                  bx:dx is the length of the midi track
             output:
                  none
        TSR_LOAD_XMIDI_PATCHES    equ 11
             This function will automatically load the patches that
             will be needed to play an xmidi track. 
             input:
                  es:di is far pointer to the event buffer including
                  the EVNT header
             output:
                  none
        TSR_LOAD_PATCH            equ 12
             This function will load a specific general midi patch. 
             input:
                  cx is the midi number of the patch to load. 
                       (0-127 is a melodic patch)
                       (128-255 is a percussion patch)
             output:
                  none
        TSR_UNLOAD_PATCH          equ 13
             This function will remove a patch loaded with 
             TSR_LOAD_PATCH.  
             input:
                  cx is the midi number of the patch to unload. 
                       (0-127 is a melodic patch)
                       (128-255 is a percussion patch)
             output:
                  none
        TSR_START_SEQUENCE        equ 14
             No parameters.  This function alerts UltraMID that on
             the next LOAD_XMIDI_PATCHES or LOAD_MIDI_PATCHES, that
             it is ok to clear the UltraSound's memory before
             loading new patches. 
        TSR_UNLOAD_ALL_PATCHES    equ 15
             No parameters.  Clears all of the patches from the
             UltraSound's memory. 
        TSR_MIDI_OUT              equ 16
             Pass one byte to the midi interpreter in UltraMID. 
             input:
                  cx - midi byte
             output:
                  none
        TSR_MIDI_OUT_STR          equ 17
             Play one or more bytes of midi data (immediately.  No
             timing information should be in this data)
             input:
                  es:di far pointer to string of midi data
                  cx is the number of bytes. 



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 21
        Chapter 6 - List of functions supported by UltraMID


             output:
                  none
        TSR_ALL_NOTES_OFF         equ 18
             start the release for any playing MIDI notes
        ; resource functions
        TSR_ALLOCATE_MEMORY       equ 19
             Allocate memory for digital audio playback.  If patches
             are preloaded using the "-c" command, there will be at
             least 8K of memory in all memory configurations. 
             input:
                  bx:dx amount of memory to allocate from
                  UltraSound's memory
             output:
                  address in ax:dx, or 0:0 in ax:dx if memory not
                  available. 
        TSR_FREE_MEMORY                   equ 20
             input:
                  bx:dx is the address of the memory on the
                  UltraSound card to free
             output:
                  none
        TSR_FREE_TSR              equ 21
             Your application shouldn't call this.  This will cause
             the TSR to stop all playing sounds, and free it's
             memory back to DOS.  inputs:
                           none
             outputs:
                           none
        TSR_SEM_ENTER             equ 22
             returns non-zero value if ok to call midi functions
             from an interrupt
        TSR_SEM_LEAVE             equ 23
             must call this after successful call to TSR_SEM_ENTER.  
        TSR_ADD_EXTERNAL_SEMAPHORE equ 24
             This call is provided if you are going to use write
             your own TSR around UltraMID, and it must be compatible
             with previous versions of that TSR, and, that TSR has a
             semaphore, AND, that TSR can report the address of it's
             own semaphore to another caller, AND it has been
             compiled in a small memory model.  This call will let
             you provide UltraMID with a far pointer to a memory
             location which UltraMID will use to let your TSR know
             that it is in a critical region of code and cannot be
             re-entered from an interrupt. 
             input:
                  bx:dx is a far pointer to your applications
                  semaphore
             output:
                  none
        TSR_CLEAR_EXTERNAL_SEMAPHORE equ 25
             Tells UltraMID to stop wasting time setting and



                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 22
        Chapter 6 - List of functions supported by UltraMID


             restoring an external semaphore set with 
             TSR_ADD_EXTERNAL_SEMAPHORE.  
             input:
                  none. 
             output:
                  none. 
        TSR_APP_START             equ 26
             This function tells UltraMID that an application will
             be using the UltraMID vector. 
        TSR_APP_END               equ 27
             This function tells UltraMID that an application is
             done using the UltraMID vector. 
        TSR_SET_MIDI_CALLBACK             equ 28
             is used to get data from the UART.  This function sets
             up a callback which will get called whenever midi data
             is available or a communications error occurs.  Your
             function should have the following prototype:

                  int far midi_receive_handler(int status, int data)

             The status variable will have the following bits set if
             there is an error.  The bit UM_ERR_FRAMING will be set
             if there is a framing error, and the bit UM_ERR_OVERRUN 
             will be set if there is a data overrun.  If neither of
             the two error bits are set, then the variable data will
             contain one midi data byte. 
             input:
                  es:di is a far pointer to your applications
                  callback function. 
             output:
                  AX will have a non-zero function if the UART could
                  not be activated or if the version of UltraMID in
                  use does not have the UART functions. 
        TSR_CLEAR_MIDI_CALLBACK                   equ 29
             is used to clear the midi interrupt and disable the
             MIDI callback function.  See TSR_SET_MIDI_CALLBACK. 
             input:
                  none. 
             output:
                  none. 














                                Forte Technologies



        UltraMID Programmer's Reference Guide                Page 23




                Chapter 7 - How to locate the TSR in memory:








        How to locate the TSR in memory:
        ************************************************************
             UltraMID tries to use an interrupt vector not in use
        between interrupt vectors 0x78 and 0x7F inclusive.  This
        vector can be overriden with the -v command line parameter.
        The segment of the vector which UltraMID uses will have the
        string "ULTRAMID" located at an offset of 0x103.  In
        pseudo-code:

        	for vector = 0x78 to 0x7f do
        	   if far_pointer(seg(vector), 0x103) == "ULTRAMID" then
        	      tsr_entry_point = far_pointer(seg(vector), off(vector))
        	   end if
        	end for

        In masm style assembly, here is a code segment that will
        find the TSR. 

        ; in your data segment
        chk_hook_str	db	'ULTRAMID'
        ultramid_hook	label   dword
        hookoff	dw ?
        hookseg dw ?
        ; in your code segment
        		push	ds
        		mov	al,078h
        UltraMID is a TSR program that has approximately 20 functions to support
        		mov	cx,6
        next_vector:
        		mov	ah,035h
        		int	21h
        ; es is segment of gf1 driver.
        ; di is offset into ULTRAMID.EXE's vector stamp.
        		mov	di,0103H
        		mov	si,offset chk_hook_str
        		push	cx
        		mov	cx,8
        		cld
        		repe	cmpsb
        		jcxz	vector_valid
        ; Setup for next vector check.



                                 Forte Technologies



        UltraMID Programmer's Reference Guide                Page 24
        Chapter 7 - How to locate the TSR in memory:


        		pop	cx
        		inc	al
        		loop	next_vector
        		jmp	vector_not_found
        vector_valid:
        		pop	cx
        		pop	ds
        		mov     hookoff, bx
        		mov     hookseg, es
        		;OK YOU GOT IT - NOW RETURN
        		mov	ax, 1
        		ret
        vector_not_found:
        		pop	ds
        		;Couldn't find it
        		xor	ax, ax
        		ret






































                                Forte Technologies


       FILE: UMWAV.C


/***************************************************************************
*       NAME:  UMWAV.C
**      COPYRIGHT:
**      "Copyright (c) 1991,1992, by Forte Technologies
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
*  CREATION DATE: 07/01/93
*--------------------------------------------------------------------------*
*     VERSION   DATE       NAME         DESCRIPTION
*>      1.0     07/01/93   umwav.c      wav player example
***************************************************************************/

#include <dos.h>
#include "ultramid.h"

/* The first half of this file contains the interface functions to  */
/* UltraMID, and the second half contains the file i/o, wav i/o, and */
/* user interface */

#pragma inline
#define MK_LONG   (long)((void _seg *)(_DX) + (void near *)(_AX))
void (far *um_hook)(void) = 0L;
short volume = 4095;
short pan = 7;
short voice;
unsigned short frequency;
int still_playing;
int user_request_stop = 0;
#define NBUFFS 10
int play_index; /* pointer to buffer which is being played */
int dma_index;  /* pointer to buffer which is being xferred */
int fill_index; /* pinter to buffer which should be filled next */
#define BS_EMPTY   0
#define BS_FILLED  1
#define BS_XFERRED 2
#define BS_PLAYING 3
unsigned char buff_status[NBUFFS];
unsigned long buff_len[NBUFFS];
#define BSIZE 4096U
#define GF1BSIZE 8192L
unsigned char buffs[NBUFFS][BSIZE];
unsigned char stbuff[GF1BSIZE/4];



       Page #1


       FILE: UMWAV.C



#pragma warn -par
int far um_callback(int reason, int voice, unsigned char far * far*buf, unsigned long far *size, unsigned short far *rate)
{
        /* this function is called from an interrupt */
        /* restore our DS register */
        asm push ds
        asm mov ax, cs
        asm mov ds, ax
        switch (reason) {
            case UM_STOP_SOUND:
                still_playing = 0;
                break;
            case UM_MORE_DATA:
                if (buff_status[dma_index] == BS_FILLED) {
                    *buf = buffs[dma_index];
                    *size = buff_len[dma_index];
                    *rate = frequency;
                    buff_status[dma_index] = BS_XFERRED;
                    dma_index = (dma_index + 1) % NBUFFS;
                    asm pop ds
                    return(1); /* return 1 when there is more data */
                               /* return 0 when done playing */
                }
                break;
            case UM_BUFFER_DONE:
                buff_status[play_index] = BS_EMPTY;
                play_index = (play_index + 1) % NBUFFS;
                if (buff_status[play_index] == BS_XFERRED) {
                        buff_status[play_index] = BS_PLAYING;
                }
                break;
        }
        asm pop ds
        return(0);
}
#pragma warn .par

int um_start_digital(struct um_sound_struct *umss)
{
        _ES = FP_SEG(umss);
        _DI = FP_OFF(umss);
        _AX = TSR_START_DIGITAL;
        (*um_hook)();
        return(_AX);
}

void um_stop_digital(int voice)
{


       Page #2


       FILE: UMWAV.C


        _CX = voice;
        _AX = TSR_STOP_DIGITAL;
        (*um_hook)();
}

void um_restart_digital(int voice)
{
        _CX = voice;
        _AX = TSR_RESTART_DIGITAL;
        (*um_hook)();
}

void um_pause_digital(int voice)
{
        _CX = voice;
        _AX = TSR_PAUSE_DIGITAL;
        (*um_hook)();
}

void um_set_pan(int voice, int pan)
{
        _CX = voice;
        _BX = pan;
        _AX = TSR_SET_PAN;
        (*um_hook)();
}

void um_set_volume(int voice, int volume)
{
        _CX = voice;
        _BX = volume;
        _AX = TSR_SET_VOLUME;
        (*um_hook)();
}

long um_allocate_memory(long mem)
{
        asm mov dx, word ptr mem;
        asm mov bx, word ptr mem+2;
        _AX = TSR_ALLOCATE_MEMORY;
        (*um_hook)();
        mem = MK_LONG;
        return(mem);
}

void um_free_memory(long mem)
{
        asm mov bx, word ptr mem+2;
        asm mov dx, word ptr mem;


       Page #3


       FILE: UMWAV.C


        _AX = TSR_FREE_MEMORY;
        (*um_hook)();
}

struct um_sound_struct umss;

/**********************************************************************/
/* file i/o functions, console functions, and other library funcs     */
/**********************************************************************/
#define CARRY_FLAG 1

void interrupt (*getvect(int int_number))()
{
        _AX = 0x3500;
        _AL = (char)int_number;
        geninterrupt(0x21);
        return(MK_FP(_ES, _BX));
}

int my_strncmp(char far *str1, char far *str2, int len)
{
        asm     mov     dx, ds
        asm     cld

        asm     les     di, str2
        asm     mov     si, di
        asm     mov     ax, len
        asm     mov     cx, ax
        asm     jcxz    done
        asm     mov     bx, ax
        asm     xor     al, al
        asm     repne   scasb
        asm     sub     bx, cx
        asm     mov     cx, bx

        asm     mov     di, si
        asm     lds     si, str1
        asm     repe    cmpsb
        asm     mov     al, [si-1]
        asm     mov     bl, es:[di-1]
        asm     xor     ah, ah 
        asm     mov     bh, ah
        asm     sub     ax, bx

        done:
        asm     mov     ds, dx
        return(_AX);
}



       Page #4


       FILE: UMWAV.C


void myputs(char *s)
{
        while (*s) {
                _BH = 0;
                _AL = *s;
                _AH = 0x0e;
                geninterrupt(0x10);
                s++;
        }
}

mygetch(void)
{
        _AX = 0x0700;
        geninterrupt(0x21);
        _AH = 0x0;
        return(_AL);
}

my_open(char *name)
{
        int save_ax, hold_ds;

        hold_ds = FP_SEG( name );               
        _DX = FP_OFF( name );           
        _AH = 0x3d; /* open file */
        _AL = 0x0; /* read only */
        
asm     push    ds;
        _DS = hold_ds;          
        geninterrupt(0x21);
asm     pop     ds;

        save_ax = _AX;

        if( _FLAGS & CARRY_FLAG ) {
                return( -1 );
        }
        return(save_ax);
}

void my_close(int handle)
{
        _BX = handle;
        _AH = 0x3e;
        geninterrupt(0x21);
}

my_read(int handle, unsigned char *buff, unsigned long lsize)


       Page #5


       FILE: UMWAV.C


{
        int hold_ds;
        unsigned int size;
        unsigned short seg, offset;
        unsigned long addr;

        while (lsize > 0) {
            size = (lsize > 32768U) ? 32768U : lsize;
            lsize -= size;
            hold_ds = FP_SEG(buff);
            _DX = FP_OFF(buff);
            _BX = handle;
            _CX = size;
            _AH = 0x3f; /* read file */
    asm push    ds;
            _DS = hold_ds;              
            geninterrupt(0x21);
    asm pop     ds;
            if (_FLAGS & CARRY_FLAG) {
                    return(0);
            }
            buff += size;
        }
        return(1);
}


/**********************************************************************/
/* Application Functions                                              */
/**********************************************************************/
struct riff_header {
        char RIFF[4];
        unsigned long file_size;
        char WAVE[4];
        char fmt[4];
        unsigned long format_size;
} riff_header;

struct format_header {
        unsigned int wFormatTag;
        unsigned int nChannels;
        unsigned long nSamplesPerSec;
        unsigned long nAvgBytesPerSec;
        unsigned int nBlockAlign;
        unsigned int nBitsPerSample;
} format_header;

struct data_header {
        unsigned char DATA[4];


       Page #6


       FILE: UMWAV.C


        unsigned long size;
} data_header;

#define MIN(a,b) ((a) < (b) ? (a) : (b))

int     far *head_keys  = (int far *)0x0040001aL;
int     far *tail_keys  = (int far *)0x0040001cL;

#define fast_kbhit() (*head_keys != *tail_keys)

void check_buffers(int fp, unsigned long *length)
{
        unsigned long size;
        static int paused=0;
        int c;

        if (fast_kbhit()) {
            if ((c=mygetch()) == 27) {
                user_request_stop = 1;
                um_stop_digital(voice);
                return;
            } else if (c == 'P' || c == 'p') {
                if (paused) {
                    um_restart_digital(voice);
                } else {
                    um_pause_digital(voice);
                }
                paused ^= 1;
            } else if (c == '>') {
                if (pan > 0) {
                    um_set_pan(voice, --pan);
                }
            } else if (c == '<') {
                if (pan < 15) {
                    um_set_pan(voice, ++pan);
                }
            } else if (c == ',') {
                volume -= 50;
                if (volume < 0) volume = 0;
                um_set_volume(voice, volume);
            } else if (c == '.') {
                volume += 50;
                if (volume > 4095) volume = 4095;
                um_set_volume(voice, volume);
            }
        }
        while (buff_status[fill_index] == BS_EMPTY) {
            /* check to see if there is any more data to read */
            if (*length == 0) break;   /* if no more data in file - return */


       Page #7


       FILE: UMWAV.C


            /* fill next buffer */
            size = MIN(BSIZE, *length);
            my_read(fp, buffs[fill_index], size);
            *length -= size;
            buff_len[fill_index] = size;
            buff_status[fill_index] = BS_FILLED;
            fill_index = (fill_index + 1) % NBUFFS;
        }
}

void play(char *filename, unsigned long gf1mem)
{
        int fp;
        int i;
        unsigned long length;
        unsigned short type=0;

        fp = my_open(filename);
        if (fp == -1) {
                myputs("Can't open file ");
                myputs(filename);
                myputs(" for playback");
                return;
        }
        my_read(fp, (unsigned char *)&riff_header, sizeof(riff_header));
        if (my_strncmp(riff_header.RIFF, "RIFF", 4) == 0 &&
            my_strncmp(riff_header.WAVE, "WAVE", 4) == 0 &&
            my_strncmp(riff_header.fmt, "fmt ", 4) == 0) {
            /* read header */
            if (my_read(fp, (unsigned char *)&format_header, riff_header.format_size) != 1) {
                myputs("File ");
                myputs(filename);
                myputs(" isn't a .WAV format file");
                my_close(fp);
                return;
            }
            if (format_header.wFormatTag != 1) {
                myputs("This player can't play ");
                myputs(filename);
                my_close(fp);
                return;
            }
            if (format_header.nChannels != 1) {
                type |= UM_STEREO;
            }
            if (format_header.nBitsPerSample == 8) {
                type |= UM_8BIT;
                type |= UM_INVERT_MSB;
            } else if (format_header.nBitsPerSample != 16) {


       Page #8


       FILE: UMWAV.C


                myputs("Can't play ");
                myputs(filename);
                myputs(".  Must be 8 or 16 bits");
                my_close(fp);
                return;
            }
            frequency = format_header.nSamplesPerSec;
            my_read(fp, (unsigned char *)&data_header, sizeof(data_header));
            if (my_strncmp(data_header.DATA, "data", 4)) {
                myputs("This player can't play ");
                myputs(filename);
                myputs(".  Can't find data");
                my_close(fp);
                return;
            }
            length = data_header.size;
            user_request_stop = 0;
            umss.stereo_mem = stbuff;
            umss.gf1mem = gf1mem;
            umss.pan = pan;
            umss.volume = volume;
            umss.sample_rate = frequency;
            umss.priority = 0;
            umss.data_type = type;
            umss.callback = um_callback;
            for (i=0; i < NBUFFS; i++) buff_status[i] = BS_EMPTY;
            fill_index = 0;
            play_index = 0;
            dma_index = 0;
            while (user_request_stop == 0 && length != 0) {
                check_buffers(fp,&length);
                buff_status[play_index] = BS_PLAYING;
                umss.sound_data = buffs[play_index];
                umss.sound_len = buff_len[play_index];
                dma_index = (dma_index + 1) % NBUFFS;
                still_playing = 1;
                voice = um_start_digital(&umss);
                if (voice == -1) break;
                while (still_playing) {
                    check_buffers(fp,&length);
                }
            }
        }
        my_close(fp);
}

int main(void)
{
        unsigned long mem;


       Page #9


       FILE: UMWAV.C


        int ret, i;
        int vector;
        char far *stamp;
        char filename[80], *cp;
        char far *command;
        unsigned char far *command_len;

        command_len = MK_FP(_psp, 0x80);
        command = MK_FP(_psp,0x82);
        if (*command_len <= 1) return(0);
        command[*command_len-1] = '\0';
        cp = filename;
        while ((*cp++ = *command++) != 0) ;
        for (vector=0x78; vector <= 0x7f; vector++) {
            um_hook = (void (far *)())getvect(vector);
            stamp = (char far *)MK_FP(FP_SEG(um_hook), 0x103);
            if (my_strncmp(stamp, "ULTRAMID", 8) == 0) break;
        }
        if (vector > 0x7f) {
            myputs("Couldn't find UltraMID\r\n");
            return(3);
        }
        mem = um_allocate_memory(0x2000L);
        if (mem == 0L) {
            myputs("UltraSound card - out of memory");
        } else {
            play(filename, mem);
            um_free_memory(mem);
        }
        return(0);
}




















       Page #10




       FILE: AUMWAV.ASM


;***************************************************************************
;       NAME:  aumwav.asm
;       COPYRIGHT:
;       "Copyright (c) 1993 by Forte Technologies
; 
;        "This software is furnished under a license and may be used,
;        copied, or disclosed only in accordance with the terms of such
;        license and with the inclusion of the above copyright notice.
;        This software or any other copies thereof may not be provided or
;        otherwise made available to any other person. No title to and
;        ownership of the software is hereby transfered."
;***************************************************************************
;  CREATION DATE: 07/01/92
;--------------------------------------------------------------------------*
;     VERSION   DATE       NAME         DESCRIPTION
;>      1.0     07/01/93   aumwav.asm   wav player example
;**************************************************************************/

;       LOCALS @@
_TEXT   segment byte public 'CODE'
_TEXT   ends
        assume  cs:_TEXT, ds:_TEXT

include ultramid.inc
riff_header struc
        rh_RIFF dd ?
        rh_file_size dd ?
        rh_WAVE dd ?
        rh_fmt dd ?
        rh_format_size dd ?
riff_header ends

format_header struc
        fh_wFormatTag dw ?
        fh_nChannels dw ?
        fh_nSamplesPerSec dd ?
        fh_nAvgBytesPerSec dd ?
        fh_nBlockAlign dw ?
        fh_nBitsPerSample dw ?
format_header ends

data_header struc
        dh_DATA dd ?
        dh_size dd ?
data_header ends

_TEXT   segment byte public 'CODE'
        org 100h
start:  call _main


       Page #1


       FILE: AUMWAV.ASM


        mov ah,4ch
        int 21h

__psp   dw ?
_um_hook        label   dword
_hookoff        dw      0
_hookseg dw     0
_volume dw      4095
_pan    dw      7
_paused  db     0
_user_request_stop      db      0

_head_keys      label   dword
        dw      1ah
        dw      40h
_tail_keys      label   dword
        dw      1ch
        dw      40h

_umss um_sound_struct <>
chk_hook_str db 'ULTRAMID',0
riffheader riff_header <>
formatheader format_header <>
dataheader data_header <>

BS_EMPTY equ 0
BS_FILLED equ 1
BS_XFERRED equ 2
BS_PLAYING equ 3
NBUFFS equ 4
BSIZE equ 8192
BSIZE_BITS equ 13

_um_callback proc       C far,reason,voice,buff:FAR PTR,bufflen:FAR PTR, bufrate:FAR PTR
        uses ds, si, di 

        mov     ax, cs
        mov     ds, ax

        mov     ax,reason
        cmp     ax,UM_STOP_SOUND
        je      short @@stop
        cmp     ax,UM_MORE_DATA
        je      short @@more_data
        cmp     ax,UM_VOICE_DONE
        je      short @@voice_done
        mov     ax, 0
        jmp     @@ret
@@stop:


       Page #2


       FILE: AUMWAV.ASM


        mov     _still_playing,0
        jmp     @@ret
@@more_data:
        mov     bx,_dma_index
        mov     ax, 0
        cmp     _buff_status[bx],BS_FILLED
        jne     short @@ret
        mov     cl,BSIZE_BITS ; find addr of buffer (_dma_index * BSIZE + offset(buffs))
        shl     bx,cl
        add     bx,offset _buffs
        ; fill in buff with next buffer to play
        les     si,buff
        mov     word ptr es:[si+2],ds
        mov     word ptr es:[si],bx
        ; fill in bufflen with size of next buffer to play
        mov     bx,_dma_index
        mov     cl,2
        shl     bx,cl
        mov     ax,_buff_len[bx+2]
        mov     dx,_buff_len[bx]
        les     bx,bufflen
        mov     word ptr es:[bx+2],ax
        mov     word ptr es:[bx],dx
        ; fill in rate with frequency of next buffer
        les     bx,bufrate
        mov     ax,_frequency
        mov     word ptr es:[bx],ax
        ; change status of _buff_status to BS_XFERRED
        mov     bx,_dma_index
        mov     byte ptr _buff_status[bx],BS_XFERRED
        ; bump _dma_index
        mov     ax,bx
        inc     ax
        mov     bx,NBUFFS
        cwd     
        idiv    bx
        mov     word ptr _dma_index,dx
        mov     ax,1
        jmp     @@ret
@@voice_done:
        mov     bx,_play_index
        mov     _buff_status[bx],BS_EMPTY
        ; bump _play_index
        mov     ax,_play_index
        inc     ax
        mov     dx, 0
        mov     cx,NBUFFS
        idiv    cx
        mov     _play_index,dx


       Page #3


       FILE: AUMWAV.ASM


        ; if buffer was xferred already change status to BS_PLAY
        mov     bx, dx
        cmp     _buff_status[bx],BS_XFERRED
        jne     short @@ret
        mov     _buff_status[bx],BS_PLAYING
@@ret:
        ret     
_um_callback    endp

_my_strncmp     proc    C near, str1:NEAR PTR, str2:NEAR PTR, len:word
        uses ds, si, di
        cld     
        mov     ax, cs
        mov     es, ax
        mov     di, str2
        mov     si, di
        mov     ax, len
        mov     cx, ax
        jcxz    short @@done
        mov     bx, ax
        xor     al, al
        repne   scasb   
        sub     bx, cx
        mov     cx, bx
        mov     di, si
        mov     si, str1
        repe    cmpsb   
        mov     al, [si-1]
        mov     bl, es:[di-1]
        xor     ah, ah 
        mov     bh, ah
        sub     ax, bx

@@done:
        ret     
_my_strncmp     endp

_myputs proc    C near string:NEAR PTR
        uses si
        mov     si,string
        jmp     short @@end_while
@@while:
        mov     bh,0
        mov     al, [si]
        mov     ah,14
        int     10h
        inc     si
@@end_while:
        cmp     byte ptr [si],0


       Page #4


       FILE: AUMWAV.ASM


        jne     short @@while
        ret     
_myputs endp

_mygetch        proc    C near
        mov     ax,700h
        int     21h
        xor     ah,ah
        ret     
_mygetch        endp

_my_open        proc    C near fname:NEAR PTR
        mov     dx,fname
        mov     ah,03dh
        mov     al,0
        int     21h
        jnc     short @@ret
        mov     ax,-1
@@ret:
        ret     
_my_open        endp

_my_close       proc    C near handle
        mov     bx, handle
        mov     ah,3eh
        int     21h
        ret     
_my_close       endp

_my_read        proc    C near handle, buff, bsize
        mov     dx,buff
        mov     bx,handle
        mov     cx,bsize
        mov     ah,3fh
        int     21h
        jc      short @@ret
        mov     ax, 1
        jmp     short @@ret1
@@ret:
        xor     ax, ax
@@ret1: ret     
_my_read        endp

_check_buffers  proc    C near fp, blength:NEAR ptr
        local bsize, c
        uses    si, di

        ; check the keyboard
        les     bx,_head_keys


       Page #5


       FILE: AUMWAV.ASM


        mov     ax, word ptr es:[bx]
        les     bx,_tail_keys
        cmp     ax,word ptr es:[bx]
        jne     @@check_key
        jmp     @@check_buffers
@@check_key:
        call    _mygetch C
        mov     si,ax
        cmp     ax,27   ; check esc key
        jne     short @@check_key_1
        mov     _user_request_stop,1
        mov     ax, TSR_STOP_DIGITAL
        mov     cx, _voice
        call    dword ptr _um_hook
        jmp     @@ret
@@check_key_1:
        cmp     si,'P'
        je      short @@p_pressed
        cmp     si,'p'
        jne     short @@check_key_2
@@p_pressed:
        cmp     _paused,0
        je      @@pause
        mov     ax, TSR_RESTART_DIGITAL
        mov     cx, _voice
        call    dword ptr _um_hook
        jmp     short @@flip_paused
@@pause:
        mov     ax, TSR_PAUSE_DIGITAL
        mov     cx, _voice
        call    dword ptr _um_hook
@@flip_paused:
        xor     _paused,1
        jmp     @@check_buffers
@@check_key_2:
        cmp     si,'>'
        jne     short @@check_key_3
        cmp     _pan,0
        jle     short @@check_key_3
        dec     _pan
@@set_pan:
        mov     bx,_pan
        mov     cx,_voice
        mov     ax, TSR_SET_PAN
        call    dword ptr _um_hook
        jmp     short @@check_buffers
@@check_key_3:
        cmp     si,'<'
        jne     short @@check_key_4


       Page #6


       FILE: AUMWAV.ASM


        cmp     _pan,15
        jge     short @@check_key_4
        inc     _pan
        jmp     short @@set_pan
@@check_key_4:
        cmp     si,','
        jne     short @@check_key_5
        sub     _volume,50
        jnc     @@set_volume
        mov     _volume,0
@@set_volume:
        mov     bx, _volume
        mov     cx, _voice
        mov     ax, TSR_SET_VOLUME
        call    dword ptr _um_hook
        jmp     short @@check_buffers
@@check_key_5:
        cmp     si,'.'
        jne     short @@check_buffers
        add     _volume,50
        cmp     _volume,4095
        jle     short @@set_volume
        mov     _volume,4095
        jmp     short @@set_volume
@@check_buffers:
        mov     di, word ptr [blength]
        jmp     @@end_while
@@while:
        ; check if next buffer is empty
        mov     ax,word ptr [di]
        or      ax,word ptr [di+2]
        jne     short @@fill_buffer
        jmp     @@ret
@@fill_buffer:
        ; bsize = MIN(BSIZE, blength)
        cmp     word ptr [di+2],0
        ja      short @@use_BSIZE
        cmp     word ptr [di],BSIZE
        jbe     short @@use_size
@@use_BSIZE:
        mov     si,BSIZE
        jmp     short @@do_read
@@use_size:
        mov     si,word ptr [di]
@@do_read:
        mov     ax,_fill_index
        mov     cl,BSIZE_BITS
        shl     ax,cl
        add     ax,offset _buffs


       Page #7


       FILE: AUMWAV.ASM


        call    _my_read C, fp, ax, si
        sub     word ptr [di],si
        sbb     word ptr [di+2],0
        mov     bx,_fill_index
        mov     cl,2
        shl     bx,cl
        mov     word ptr _buff_len[bx+2],0
        mov     word ptr _buff_len[bx],si
        mov     bx,_fill_index
        mov     _buff_status[bx],BS_FILLED
        mov     ax,bx
        inc     ax
        mov     bx,NBUFFS
        cwd     
        idiv    bx
        mov     _fill_index,dx
@@end_while:
        mov     bx,_fill_index
        cmp     _buff_status[bx],BS_EMPTY
        jne     @@ret
        jmp     @@while
@@ret:  ret     
_check_buffers  endp

_play   proc    C near filename:NEAR PTR, gf1mem:dword
        local fp, i, blength:dword, btype
        uses si, di

        mov     btype, 0
        call    _my_open C,[filename]
        mov     fp, ax
        cmp     ax, -1
        jne     short @@get_riff_header
        mov     ax,offset msg_cant_open
        call    _myputs C,ax
        call    _myputs C,[filename]
        mov     ax,offset msg_cant_open1
        call    _myputs C,ax
        jmp     @@ret
@@get_riff_header:
        mov     ax, offset riffheader
        mov     cx, size riffheader
        call    _my_read C, fp, ax, cx
        mov     bx, offset riffheader.rh_RIFF
        mov     cx, offset msg_RIFF
        call    _my_strncmp C, bx, cx, 4
        or      ax,ax
        je      @@2
        jmp     @@not_wav


       Page #8


       FILE: AUMWAV.ASM


@@2:
        lea     bx, riffheader.rh_WAVE
        lea     cx, msg_WAVE
        mov     ax,4
        call    _my_strncmp C, bx, cx, 4
        or      ax,ax
        je      @@3
        jmp     @@not_wav
@@3:
        lea     bx, riffheader.rh_fmt
        lea     cx, msg_fmt
        mov     ax,4
        call    _my_strncmp C, bx, cx, 4
        or      ax,ax
        je      @@4
        jmp     @@not_wav
@@4:
        mov     bx, word ptr riffheader.rh_format_size
        mov     ax, offset formatheader
        call    _my_read C, fp, ax, bx
        cmp     ax,1
        je      short @@check_tag
@@not_wav:
        mov     ax, offset msg_File
        call    _myputs C, ax
        call    _myputs C, filename
        mov     ax, offset msg_isnt_wave
        call    _myputs C, ax
@@close:
        call    _my_close C, fp
        jmp     @@ret
@@check_tag:
        cmp     word ptr formatheader.fh_wFormatTag,1
        je      short @@check_nchannels
        mov     ax, offset msg_cant_play
        call    _myputs C, ax
        call    _myputs C, filename
        jmp     short @@close
@@check_nchannels:
        cmp     word ptr formatheader.fh_nChannels,1
        je      @@check_nbits
        or      btype, UM_STEREO
@@check_nbits:
        cmp     word ptr formatheader.fh_nBitsPerSample,8
        jne     short @@check_16bits
        or      btype, (UM_8BIT OR UM_INVERT_MSB)
        jmp     short @@get_frequency
@@check_16bits:
        cmp     word ptr formatheader.fh_nBitsPerSample,16


       Page #9


       FILE: AUMWAV.ASM


        je      short @@get_frequency
        mov     ax, offset msg_cant_play
        call    _myputs C, ax
        call    _myputs C, filename
        mov     ax, offset msg_bad_type
        call    _myputs C, ax
        jmp     short @@close
@@get_frequency:
        mov     ax,word ptr formatheader.fh_nSamplesPerSec
        mov     word ptr _frequency,ax
        mov     ax, offset dataheader
        mov     bx, size dataheader
        call    _my_read C, fp, ax, bx
        lea     bx, dataheader.dh_DATA
        mov     ax, offset msg_data
        call    _my_strncmp C, bx, ax, 4
        or      ax,ax
        je      short @@get_length
        mov     ax, offset msg_cant_play
        call    _myputs C, ax
        call    _myputs C, filename
        mov     ax, offset msg_no_data
        call    _myputs C, ax
        jmp     @@close
@@get_length:
        mov     ax,word ptr dataheader.dh_size+2
        mov     dx,word ptr dataheader.dh_size
        mov     word ptr [blength+2],ax
        mov     word ptr [blength],dx
        mov     _user_request_stop,0
        mov     _umss.um_stereo_mem_seg,ds
        mov     _umss.um_stereo_mem_off,offset _stbuff
        mov     ax,word ptr [gf1mem+2]
        mov     dx,word ptr [gf1mem]
        mov     word ptr _umss.um_gf1mem+2,ax
        mov     word ptr _umss.um_gf1mem,dx
        mov     al,byte ptr _pan
        mov     byte ptr _umss.um_pan,al
        mov     ax,word ptr _volume
        mov     word ptr _umss.um_volume,ax
        mov     ax,word ptr _frequency
        mov     word ptr _umss.um_sample_rate,ax
        mov     word ptr _umss.um_priority,0
        mov     al,byte ptr btype
        mov     byte ptr _umss.um_data_type,al
        mov     word ptr _umss.um_callback_addr_seg,cs
        mov     word ptr _umss.um_callback_addr_off,offset _um_callback
        mov     bx,0
@@empty_buffs:


       Page #10


       FILE: AUMWAV.ASM


        mov     _buff_status[bx],BS_EMPTY
        inc     bx
        cmp     bx, NBUFFS
        jne     short @@empty_buffs
        mov     word ptr _fill_index,0
        mov     word ptr _play_index,0
        mov     word ptr _dma_index,0
        jmp     @@end_while
@@while:
        lea     ax,blength
        call    _check_buffers C, fp, ax
        mov     bx,_play_index
        mov     _buff_status[bx],BS_PLAYING
        mov     ax,bx
        mov     cl,BSIZE_BITS
        shl     ax,cl
        add     ax,offset _buffs
        mov     word ptr _umss.um_sound_data_seg,ds
        mov     word ptr _umss.um_sound_data_off,ax
        mov     cl,2
        shl     bx,cl
        mov     ax,word ptr _buff_len[bx+2]
        mov     dx,word ptr _buff_len[bx]
        mov     word ptr _umss.um_sound_len+2,ax
        mov     word ptr _umss.um_sound_len,dx
        mov     ax,_dma_index
        inc     ax
        mov     bx,NBUFFS
        cwd     
        idiv    bx
        mov     _dma_index,dx
        mov     word ptr _still_playing,1
        mov     ax, cs
        mov     es, ax
        mov     di, offset _umss
        mov     ax, TSR_START_DIGITAL
        call    _um_hook
        mov     word ptr _voice,ax
        cmp     ax,-1
        je      short @@done
        jmp     @@check_playing
@@keep_on_checking:
        lea     ax, blength
        call    _check_buffers C, fp, ax
@@check_playing:
        cmp     word ptr _still_playing,0
        jne     short @@keep_on_checking
@@end_while:
        cmp     _user_request_stop,0


       Page #11


       FILE: AUMWAV.ASM


        jne     short @@done
        mov     ax,word ptr [blength]
        or      ax,word ptr [blength+2]
        je      @@done
        jmp     @@while
@@done:
        jmp     @@close
@@ret:  ret
_play   endp

_main   proc    C near
        local   gf1mem:dword
        mov     ax, cs
        mov     ds, ax
        mov     ax, es
        mov     __psp, ax

        mov     bh, 0
        mov     di, 80h
        mov     bl, byte ptr es:[di]
        cmp     bl, 1
        jg      @@find_filename
        jmp     @@ret
@@find_filename:
        dec     bx
        mov     si, 82h
        mov     byte ptr es:[bx+si], 0
; copy file name into this segment
        push    ds
        push    ds
        mov     ax, es
        mov     ds, ax
        pop     es
        mov     di, offset _filename
        mov     cx, 80
        cld
        rep     movsb
        pop     ds

; find ultramid's entry point

        mov     al,078h
        mov     cx,6
@@next_vector:
        mov     ah,035h
        int     21h
; es is segment of gf1 driver.
; di is Offset into UltraMID's vector stamp.
        mov     di,0103H


       Page #12


       FILE: AUMWAV.ASM



        mov     si,offset chk_hook_str
        push    cx
        mov     cx,8
        cld
        repe    cmpsb
        jcxz    @@vector_valid
; Setup for next vector check.
        pop     cx
        inc     al
        loop    @@next_vector
        jmp     @@vector_not_found
@@vector_valid:
        pop     cx

        mov     ah,035h
        int     21h
        mov     [_hookoff], bx
        mov     [_hookseg], es

        mov     ax, TSR_APP_START
        call    dword ptr _um_hook

        xor     bx,bx
        mov     dx,8192
        mov     ax, TSR_ALLOCATE_MEMORY
        call    dword ptr _um_hook
        mov     word ptr [gf1mem+2],dx
        mov     word ptr [gf1mem],ax
        or      ax,dx
        je      short @@no_memory
        mov     ax, offset _filename
        call    _play C, ax, [gf1mem]
        mov     dx, word ptr [gf1mem]
        mov     bx, word ptr [gf1mem+2]
        mov     ax, TSR_FREE_MEMORY
        call    dword ptr _um_hook
        mov     ax, TSR_APP_END
        call    dword ptr _um_hook
        jmp     short @@ret
@@no_memory:
        mov     ax, offset msg_no_mem
        call    _myputs C, ax
        jmp     short @@ret
@@vector_not_found:
        mov     ax, offset msg_no_um
        call    _myputs C, ax
        jmp     short @@ret
@@ret:  


       Page #13


       FILE: AUMWAV.ASM


        ret     
_main   endp

msg_cant_open   db      "Can't open file ", 0
msg_cant_open1  db      ' for playback',0
msg_RIFF        db      'RIFF',0
msg_WAVE        db      'WAVE',0
msg_fmt         db      'fmt ',0
msg_File        db      'File ',0
msg_isnt_wave   db      " isn't a .WAV format file",0
msg_cant_play   db      "This player can't play ",0
msg_bad_type    db      '.  Must be 8 or 16 bits',0
msg_data        db      'data',0
msg_no_data     db      ".  Can't find data",0
msg_no_um       db      "Couldn't find UltraMID",13,10,0
msg_no_mem      db      'UltraSound card - out of memory',13,10,0
_filename db    80 dup (?)
_stbuff db      2048 dup (?)
_buffs  db      BSIZE*NBUFFS dup (?)
_buff_len       label   word
        db      40 dup (?)
_buff_status    label   byte
        db      NBUFFS dup (?)
_fill_index     dw ?
_dma_index      dw ?
_play_index     dw ?
_still_playing  dw ?
_frequency      dw ?
_voice          dw ?
_TEXT   ends

        end start



















       Page #14




       FILE: TOTSR.C


/*
** Copyright (C) 1993 Forte.  All rights reserved
** Written late one night 6/27/93.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "midi.h"
#include "totsr.h"
#include "ultramid.h"

extern int control_break;

/* interrupt clock stuff */
static void interrupt (*orig_clock)(void) = 0L;
static void interrupt clock(void);

static unsigned long clock_interval = 8334L; /* microseconds */
#define CLOCK_DIVISOR 9944U
#define ORIG_CLOCK_DIVISOR 0U

/* the timer interrupt is on vector 0x08 */
#define TIMER 0x08

void (far *um_hook)(void) = 0L;

int     um_data_out( int byte )
{
        if (um_hook) {
            _AX = TSR_MIDI_OUT;
            _CX = byte;
            (*um_hook)();
        }
        return(0);
}

void    um_pitch_bend( int channel, int msb, int lsb )
{
        um_data_out( (pitch_wheel+channel) );
        um_data_out( msb );
        um_data_out( lsb );
}

void    um_program( int channel, int prog )
{
        um_data_out( (program_chng+channel) );
        um_data_out( prog );


       Page #1


       FILE: TOTSR.C


}

void    um_chanpressure( int channel, int pitch, int pressure )
{
        um_data_out( (channel_aftertouch+channel) );
        um_data_out( pitch );
        um_data_out( pressure );
}

void    um_pressure( int channel, int pitch, int pressure )
{
        um_data_out( (poly_aftertouch+channel) );
        um_data_out( pitch );
        um_data_out( pressure );
}

void    um_parameter( int channel, int control, int value )
{
        um_data_out( (control_change+channel) );
        um_data_out( control );
        um_data_out( value );
}

int     um_note_on( int channel, int note, int velocity )
{
        int error;

        error = um_data_out( (note_on+channel) );
        error += um_data_out( note );
        error += um_data_out( velocity );
        return(error);
}

void    um_note_off( int channel, int note, int velocity )
{
        um_data_out( (note_off+channel) );
        um_data_out( note );
        um_data_out( velocity );
}

int     reset_um( )
{
        int i;

        if (um_hook) {
            _AX = TSR_ALL_NOTES_OFF;
            (*um_hook)();
            for (i=0; i < 16; i++) {
                um_parameter(i, 0x78, 0); /* all sounds off */


       Page #2


       FILE: TOTSR.C


                um_parameter(i, 0x79, 0); /* reset all controllers */
                um_parameter(i, 100, 0); /* RPN MSB 0 */
                um_parameter(i, 101, 0); /* RPN LSB 0 */
                um_parameter(i, 6, 2); /* Pitch Bend Sensitivity MSB */
                um_parameter(i, 38, 0); /* Pitch Bend Sensitivity LSB */
            }
        }
        return(1);
}

static unsigned long us_timer = 0L;
static unsigned long timer = 0L;
static unsigned long tempo = 4800L;

static void interrupt clock()
{
        short t;

        us_timer += clock_interval;
        if (us_timer > tempo) {
                t = us_timer / tempo;
                us_timer -= (t * tempo);
                timer += t;
        }
        if (orig_clock) (*orig_clock)();
}

void um_set_tempo(us, divisor)
unsigned long us;
short divisor;
{
        /*
        ** the tempo and divisor values are described in the Standard Midi
        ** File specification 1.0
        */
        int smpte_format, smpte_resolution;

        if (divisor > 0) {
                tempo = us / divisor;
        } else {
                smpte_format = (-divisor >> 8) & 0x7f;
                smpte_resolution = divisor & 0x7f;
                tempo = 1000000 / (smpte_format * smpte_resolution);
        }
        if (tempo <= 0) tempo = 1;
}

void um_reset_tick_counter()
{


       Page #3


       FILE: TOTSR.C


        timer = 0L;
        us_timer = 0L;
}

int um_wait_for ( unsigned long some_time )
{
        if (kbhit() || control_break) return(1);
        while (some_time > timer) if (kbhit() || control_break) return(1);
        return(0);
}

#pragma warn -par
int     um_init_hardware(struct MIDILIB *ml)
{
        int vector, i;
        char far *stamp;
        struct track *track;
        unsigned long length;

        for (vector=0x78; vector <= 0x7f; vector++) {
            um_hook = (void (far *)())getvect(vector);
            stamp = (char far *)MK_FP( FP_SEG(um_hook), 0x103 );
            if (strncmp(stamp, "ULTRAMID", 8) == 0) break;
        }
        if (vector <= 0x7f) {
            midi_message("UltraMID is now loading instrument patches");
            if (um_hook) {
                _AX = TSR_APP_START;
                (*um_hook)();
            }
            reset_um();
            if (um_hook) {
                for (track=ml->Mp_tracks; track; track = track->next_track) {
                    _ES = FP_SEG(track->data);
                    asm push es
                    _DI = FP_OFF(track->data);
                    asm push di
                    length = track->length;
                    asm mov bx, word ptr length+2
                    asm mov dx, word ptr length
                    asm pop di
                    asm pop es
                    _AX = TSR_LOAD_MIDI_PATCHES;
                    (*um_hook)();
                }
                _AX = TSR_START_SEQUENCE;
                (*um_hook)();
            }
        } else {


       Page #4


       FILE: TOTSR.C


            um_hook = 0L;
            midi_error("Couldn't find UltraMID");
            return(1);
        }
        return(0);
}
#pragma warn .par

void    um_cleanup( void )
{
        reset_um();
        if (um_hook) {
            _AX = TSR_APP_END;
            (*um_hook)();
        }
}

void    um_init_timers(void)
{
        orig_clock = getvect(TIMER);
        setvect(TIMER, clock);
        outportb(0x43, 0x36);
        outportb(0x40, CLOCK_DIVISOR & 0xff);
        outportb(0x40, (CLOCK_DIVISOR & 0xff00) >> 8);
}

void    um_cleanup_timers(void)
{
        outportb(0x43, 0x36);
        outportb(0x40, ORIG_CLOCK_DIVISOR & 0xff);
        outportb(0x40, (ORIG_CLOCK_DIVISOR & 0xff00) >> 8);
        if (orig_clock) setvect(TIMER, orig_clock);
}


















       Page #5




       FILE: UMUART.C


/***************************************************************************
*       NAME:  UMIDI.C
**      COPYRIGHT:
**      "Copyright (c) 1991,1992, by Forte Technologies
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
*  CREATION DATE: 09/11/93
*--------------------------------------------------------------------------*
*     VERSION   DATE       NAME         DESCRIPTION
*>      1.0     09/11/93   umidi.c      example of how to use midi uart
***************************************************************************/

#include <conio.h>
#include <dos.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include "ultramid.h"

/* MIDI status commands most significant bit is 1 */
#define note_off                0x80
#define note_on                 0x90
#define poly_aftertouch         0xa0
#define control_change          0xb0
#define program_chng            0xc0
#define channel_aftertouch      0xd0
#define pitch_wheel             0xe0
#define system_exclusive        0xf0

#pragma inline

extern unsigned __stklen = 4096;

int volume = 3900;
volatile int user_is_tired = 0;

/****************************************************************************/
/* TRAP CONTROL-C AND CONTROL-BREAK *****************************************/
/****************************************************************************/
int cbreak()


       Page #1


       FILE: UMUART.C


{
        user_is_tired = 1;
        return(1);
}

/****************************************************************************/
/* ULTRAMID INTERFACE FUNCTIONS *********************************************/
/****************************************************************************/
int (far *um_hook)(void) = 0L;

/* send a midi byte to to UltraMID */
int     um_data_out( int byte )
{
        if (um_hook) {
            _AX = TSR_MIDI_OUT;
            _CX = byte;
            (*um_hook)();
        }
        return(0);
}

/* Tell UltraMID that this application is going to be using it */
void um_app_start(void)
{
        if (um_hook) {
            _AX = TSR_APP_START;
            (*um_hook)();
        }
}

/* Tell UltraMID that this application is done using it */
void um_app_end(void)
{
        if (um_hook) {
            _AX = TSR_APP_END;
            (*um_hook)();
        }
}

/* attempt to load a patch.  0-127 are melodic instruments */
/*                           128-255 are percussion */
int     um_load_patch( int prog )
{
        int ax;

        printf("loading patch %d\n", prog);
        _CX = prog;
        _AX = TSR_LOAD_PATCH;
        ax = (*um_hook)();


       Page #2


       FILE: UMUART.C


        return(ax);
}

void    um_unload_patch( int prog )
{
        _CX = prog;
        _AX = TSR_UNLOAD_PATCH;
        (*um_hook)();
}

/* Turn all currently playing notes off.  You should call this function if */
/* there is a MIDI framing error or a data overrun error */
void    um_all_notes_off( void )
{
        _AX = TSR_ALL_NOTES_OFF;
        (*um_hook)();
}

/* send a controller message to UltraMID */
void um_parameter(int chan, int controller, int value)
{
        um_data_out(control_change|chan);
        um_data_out(controller);
        um_data_out(value);
}

/* Reset the UltraMID interpreter to its default state */
void    reset_um(void)
{
        int i;

        if (um_hook) {
            _AX = TSR_ALL_NOTES_OFF;
            (*um_hook)();
            for (i=0; i < 16; i++) {
                um_parameter(i, 0x78, 0); /* all sounds off */
                um_parameter(i, 0x79, 0); /* reset all controllers */
                um_parameter(i, 100, 0); /* RPN MSB 0 */
                um_parameter(i, 101, 0); /* RPN LSB 0 */
                um_parameter(i, 6, 2); /* Pitch Bend Sensitivity MSB */
                um_parameter(i, 38, 0); /* Pitch Bend Sensitivity LSB */
            }
        }
}
 
/* Find the UltraMID TSR and set up a dynamic link to it */
int um_link(void)
{
        int vector;


       Page #3


       FILE: UMUART.C


        char far *stamp;

        for (vector=0x78; vector <= 0x7f; vector++) {
            um_hook = (int (far *)())getvect(vector);
            stamp = (char far *)MK_FP( FP_SEG(um_hook), 0x103 );
            if (strncmp(stamp, "ULTRAMID", 8) == 0) break;
        }
        if (vector <= 0x7f) {
            um_app_start();
            reset_um();
        } else {
            um_hook = 0L;
            printf("Couldn't find UltraMID");
            return(1);
        }
        return(0);
}

/* Set up a callback to receive MIDI data from the UART */
int     um_set_midi_callback( int (far *callback)() )
{
        _ES = FP_SEG(callback);
        _DI = FP_OFF(callback);
        _AX = TSR_SET_MIDI_CALLBACK;
        return((*um_hook)());
}

/* Reset the midi UART callback */
void    um_clear_midi_callback( void )
{
        um_set_midi_callback((int (far *)())0);
}

/* unlink from UltraMID */
void um_unlink(void)
{
        int i;

        um_app_end();
}

/* The midi interpreter calls this function when it completely parses a */
/* channel message */
/* You can use this function to keep track of all midi messages that are */
/* being sent to ultramid, and to make sure patches are loaded before */
/* a program change occurs */
#pragma warn -par
void chanmessage(int status,int c1,int c2)
{


       Page #4


       FILE: UMUART.C


        int chan = status & 0xf;

        switch ( status & 0xf0 ) {
            case note_off:
                break;
            case note_on:
                if (chan == 9) {
                    if (um_load_patch(c1+128)) {
                        printf("couldn't load patch %d\n", c1+128);
                    }
                }
                break;
            case poly_aftertouch:
                break;
            case control_change:
                break;
            case pitch_wheel:
                break;
            case program_chng:
                if (chan != 9) {
                    if (um_load_patch(c1)) {
                        printf("couldn't load patch %d\n", c1);
                    }
                }
                break;
            case channel_aftertouch:
                break;
        }
        return;
}
#pragma warn .par
        
static enum { NORMAL, INCHAN1, INCHAN2, INSYSEX, INSYS1, INSYS2 } midi_mode;

/* This array is indexed by the high half of a status byte.  It's */
/* value is either the number of bytes needed (1 or 2) for a channel */
/* message, or 0 (meaning it's not  a channel message). */
static int chantype[] = {
        0, 0, 0, 0, 0, 0, 0, 0,         /* 0x00 through 0x70 */
        2, 2, 2, 2, 1, 1, 2, 0          /* 0x80 through 0xf0 */
};

/* This function interprets midi data received from the UART. */
void midi_out(int c)
{
        static int c1, c2;
        static int needed;
        static int status = note_on;    /* status value (e.g. 0x90==note-on) */
        static int running = 0; /* 1 when running status used */


       Page #5


       FILE: UMUART.C



        if (c >= 0xf8 && c <= 0xff) { /* real time message */
                return;
        }
        if (midi_mode != INSYSEX) {
            if (c & 0x80) { /* new status byte */
                    status = c;
                    running = 0;
                    midi_mode = NORMAL;
            } else {
                    running = 1;
            }
        }
        switch (midi_mode) {
            case NORMAL:
                needed = chantype[(status>>4) & 0xf];

                if (needed) {   /* is it a channel message? */
                    if (running) {
                        c1 = c;
                        if (needed == 1) {
                            chanmessage( status, c1, 0 );
                            midi_mode = NORMAL;
                            break;
                        } else {
                            midi_mode = INCHAN2;
                            break;
                        }
                    } else {
                            midi_mode = INCHAN1;
                            break;
                    }
                } else {
                    switch (status) {
                    case 0xf0:  /* start of system exclusive */
                            midi_mode = INSYSEX;
                            break;
                    case 0xf1:  /* midi time code quarter frame */
                    case 0xf3:  /* song select */
                            midi_mode = INSYS1;
                            break;
                    case 0xf2:  /* song position pointer */
                            midi_mode = INSYS2;
                            break;
                    case 0xf4:  /* undefined */
                    case 0xf5:
                    case 0xf6:  /* tune request */
                    case 0xf7:  /* End of system exclusive */
                            midi_mode = NORMAL;


       Page #6


       FILE: UMUART.C


                            break;
                    }
                    if (running) {
                            midi_out(c);
                    }
                }
                return;
            case INCHAN1:
                    c1 = c;
                    if (needed == 1) {
                            chanmessage( status, c1, 0 );
                            midi_mode = NORMAL;
                            break;
                    } else {
                        midi_mode = INCHAN2;
                    }
                    break;
            case INCHAN2:
                    running = 0;
                    c2 = c;
                    chanmessage( status, c1, c2 );
                    midi_mode = NORMAL;
                    break;
            case INSYSEX:
                    if (c == 0xf7) midi_mode = NORMAL;
                    break;
            case INSYS1:
                    midi_mode = NORMAL;
                    break;
            case INSYS2:
                    midi_mode = INSYS1;
                    break;
            default:
                    midi_mode = NORMAL;
        }
        return;
}

#define MIDI_BUFF 512
unsigned char midi_data[MIDI_BUFF];
int midi_in_index=0;
int midi_out_index=0;
int midi_error = 0;

/* like the library function kbhit(), but for midi data */
int midi_hit(void)
{
        if (midi_error) {
                printf("midi data error\n");


       Page #7


       FILE: UMUART.C


                um_all_notes_off();
                midi_error = 0;
        }
        return (midi_in_index != midi_out_index);
}

/* get a character from the MIDI FIFO -- foreground function */
int midi_getc(void)
{
        int c;

        asm pushf;
        disable();
        c = midi_data[midi_out_index];
        midi_out_index = (midi_out_index + 1) % MIDI_BUFF;
        asm popf;
        return(c);
}

/* get a character from the MIDI UART -- called from an interrupt handler */
/* be careful not to make any DOS calls or library calls from this function. */
/* Also, don't make any calls to UltraMID from this function */
int far midi_receive(int midi_status, int data)
{
        int next_index;

        asm pushf;
        disable();
        asm push ds
        asm mov ax, DGROUP
        asm mov ds, ax
        if (midi_status & (UM_ERR_FRAMING|UM_ERR_OVERRUN)) {
error:
                midi_error = 1;
                midi_in_index = 0;
                midi_out_index = 0;
        } else {
                next_index = (midi_in_index + 1) % MIDI_BUFF;
                if (midi_out_index == next_index) {
                        /* buffer full */
                        goto error;
                } else {
                        midi_data[midi_in_index] = data;
                        midi_in_index = next_index;
                }
        }
        asm pop ds
        asm popf
        return(1);


       Page #8


       FILE: UMUART.C


}

void help( void )
{
        printf("Press 'c' to clear all loaded patches\n");
        printf("Press ESC to exit program\n");
}

void unload_patches(void)
{
        int i;

        for (i=0; i < 255; i++) {
                um_unload_patch(i);
        }
}

#pragma warn -par
int main(int argc, char *argv[])
{
        int c;

        ctrlbrk(cbreak);
        if (um_link()) exit(1);
        if (um_set_midi_callback(midi_receive)) {
            printf("Couldn't activate the UltraSound UART\n");
            um_unlink();
            exit(2);
        }
        user_is_tired = 0;
        while (!user_is_tired) {
            if (midi_hit()) {
                c = midi_getc();
                midi_out(c);
                um_data_out(c);
            }
            if (kbhit()) {
                c = getch();
                switch (c) {
                    case '?' :
                        help();
                        break;
                    case 'c' :
                        unload_patches();
                        break;
                    case 27:    
                        user_is_tired = 1;
                        break;
                }


       Page #9


       FILE: UMUART.C


            }
        }
        unload_patches();
        um_clear_midi_callback();
        reset_um(); /* reset ultramid and turn off all playing notes */
        um_unlink();
        return(0);
}
#pragma warn .par










































       Page #10